/* eslint-disable @typescript-eslint/no-explicit-any */
import { context, noWindowFound } from './context';
import { NS, ns } from './namespace';
import { BaseElement, ChildType, JSXClass, JSXFunction, ObjectProps } from './types';
import { appendAll, isComponentClass, isHTMLElementClass, isMathMLElementClass, isSVGElementClass, jsxGen } from './utils';

function jx<T extends HTMLElement>(
	tag: JSXClass<any> | (new () => T) | string,
	props: ObjectProps<any> | null,
	key?: string
): BaseElement;
function jx(tag: typeof Fragment, props: ObjectProps<any> | null): DocumentFragment;
function jx(tag: JSXFunction, props: ObjectProps<any> | null, key?: string): BaseElement | DocumentFragment;
function jx<T extends HTMLElement>(
	tag: JSXFunction | JSXClass<any> | (new () => T) | string,
	props: ObjectProps<any> | null,
	key?: string
): BaseElement | DocumentFragment;
function jx<T extends HTMLElement>(
	tag: JSXFunction | JSXClass<any> | (new () => T) | string,
	props: Record<string, any> | null,
	key?: string
): BaseElement | DocumentFragment {
	props = props ?? {};
	if (key !== undefined) {
		props.key = key;
	}
	let elm: BaseElement | DocumentFragment;
	if (typeof tag === 'function') {
		if (isComponentClass(tag)) {
			const obj = new tag(props);
			elm = obj.el;
			props.ref?.(obj);
		} else if (isHTMLElementClass<T>(tag) || isSVGElementClass<T>(tag) || isMathMLElementClass<T>(tag)) {
			elm = new tag();
			jsxGen(elm, props);
			props.ref?.(elm);
		} else {
			elm = tag(props);
		}
	} else {
		if (!context) {
			noWindowFound();
		}
		const extendFrom = props.is as string | undefined;
		props.is = undefined;
		const doc = context.document;
		if (tag.startsWith('math:')) {
			elm = doc.createElementNS(NS.MATH, tag.substring(5));
		} else if (tag.startsWith('svg:')) {
			elm = doc.createElementNS(NS.SVG, tag.substring(4));
		} else if (tag.startsWith('html:')) {
			elm = doc.createElementNS(NS.HTML, tag.substring(5), extendFrom ? { is: extendFrom } : undefined) as BaseElement;
		} else if (ns === NS.HTML) {
			elm = doc.createElement(tag, extendFrom ? { is: extendFrom } : undefined) as BaseElement;
		} else {
			elm = doc.createElementNS(ns, tag, extendFrom ? { is: extendFrom } : undefined) as BaseElement;
		}
		jsxGen(elm, props);
		props.ref?.(elm);
	}
	return elm;
}

export { jx as jsxs, jx as jsx };

export function Fragment(props: { children?: ChildType | ChildType[] }) {
	if (!context) {
		noWindowFound();
	}
	const { children = [] } = props;
	const fragment = context.document.createDocumentFragment();
	appendAll(fragment, children);
	return fragment;
}
